package cz.data.common.mark.manage;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import cz.data.common.database.CacheDataSourceBean;
import cz.data.common.database.DbDialect;
import cz.data.common.database.DialectFactory;
import cz.data.common.database.utils.DbDialectUtils;
import cz.data.common.exception.DataException;
import cz.data.common.mark.DbRow;
import cz.data.common.mark.mybatis.DynamicMapperUtils;
import cz.data.common.support.db.DbProperty;
import cz.data.common.support.db.core.DbColumn;
import cz.data.common.support.db.core.DbTable;
import cz.data.common.support.db.manage.Condition;
import cz.data.common.support.db.manage.DbManage;
import cz.data.common.support.db.manage.SearchUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.jdbc.core.JdbcTemplate;

import javax.sql.DataSource;
import java.util.List;
import java.util.Objects;
import java.util.Properties;
import java.util.stream.Collectors;

@Slf4j
public class JdbcDbManage implements DbManage {

    private final DbProperty property;
    private final JdbcTemplate jdbcTemplate;
    private final JdbcManageDialect dialect;
    private final DbDialect dbDialect;

    public JdbcDbManage(DbProperty property) {
        this.property = property;
        this.dialect = JdbcManageDialect.create(property.getDbTypeEnum());
        DataSource dataSource = CacheDataSourceBean.createDataSource(property);
        this.jdbcTemplate = new JdbcTemplate(dataSource);
        this.dbDialect = DialectFactory.getDialect(property.getDbTypeEnum());
    }

    @Override
    public List<DbTable> getTables(String dbName, String schemaName) {
        String sql = dbDialect.tables(dbName, schemaName);
        return jdbcTemplate.query(sql, dbDialect.tableMapper());
    }

    @Override
    public List<DbColumn> getTableColumns(String dbName, String schemaName, String tableName) {
        String sql = dbDialect.columns(dbName, schemaName, tableName);
        log.debug("查询列SQL: " + sql);
        return jdbcTemplate.query(sql, dbDialect.columnMapper());
    }

    @Override
    public boolean createTable(DbTable table, Properties properties) {
        String settings = this.dialect.properties(table, properties);
        table.setSettings(settings);
        log.debug("配置: {}", settings);
        DynamicMapperUtils.consumerMapper(property, mapper -> mapper.createTable(table));
        return true;
    }

    @Override
    public boolean deleteTable(DbTable table) {
        DynamicMapperUtils.consumerMapper(property, mapper -> mapper.dropTable(table));
        return true;
    }

    @Override
    public boolean insertData(DbTable table, JSONObject record) {
        DbRow dbRow = builder(table, record);
        this.dialect.preProcessData(table, dbRow.getRow());
        Integer rows = DynamicMapperUtils.mapMapper(property, mapper -> mapper.insertData(dbRow));
        return Objects.nonNull(rows) && rows > 0;
    }

    @Override
    public boolean updateData(DbTable table, JSONObject record) {
        DbRow dbRow = builder(table, record);
        this.dialect.preProcessData(table, dbRow.getRow());
        Wrapper<?> wrapper = whereBuilder(table.getColumns(), record, false);
        Integer rows = DynamicMapperUtils.mapMapper(property, mapper -> mapper.updateData(dbRow, wrapper));
        return Objects.nonNull(rows) && rows > 0;
    }

    @Override
    public boolean removeData(DbTable table, JSONObject record) {
        DbRow dbRow = builder(table, record);
        this.dialect.preProcessData(table, dbRow.getRow());
        Wrapper<?> wrapper = whereBuilder(table.getColumns(), record, false);
        Integer rows = DynamicMapperUtils.mapMapper(property, mapper -> mapper.deleteData(dbRow, wrapper));
        return Objects.nonNull(rows) && rows > 0;
    }

    @Override
    public IPage<JSONObject> getPageData(DbTable table, List<String> fields, List<Condition> conditions, Page<JSONObject> page) {
        String tableName = table.getTableName();
        for (Condition condition : conditions) {
            if (SearchUtil.isNotBlank(condition.getValue())) {
                JSONObject params = new JSONObject();
                params.put(condition.getColumn(), condition.getValue());
                this.dialect.preProcessData(table, params);
                Object value = params.get(condition.getColumn());
                condition.setValue(value);
            }
            if (SearchUtil.isNotBlank(condition.getLeftValue())) {
                JSONObject params = new JSONObject();
                params.put(condition.getColumn(), condition.getLeftValue());
                this.dialect.preProcessData(table, params);
                Object value = params.get(condition.getColumn());
                condition.setLeftValue(value);
            }
            if (SearchUtil.isNotBlank(condition.getRightValue())) {
                JSONObject params = new JSONObject();
                params.put(condition.getColumn(), condition.getRightValue());
                this.dialect.preProcessData(table, params);
                Object value = params.get(condition.getColumn());
                condition.setRightValue(value);
            }
        }
        QueryWrapper<?> wrapper = SearchUtil.parseWhereSql(conditions);
        List<String> selectFields = fields.stream()
                .map(f -> DbDialectUtils.fieldAddQuoting(property.getDbType(), f))
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(selectFields)) {
            wrapper.select("*");
        } else {
            wrapper.select(selectFields);
            wrapper.orderByDesc(selectFields.get(0));
        }
        return DynamicMapperUtils.mapMapper(property, mapper -> mapper.getPageModelDatas(tableName, wrapper, page));
    }

    @Override
    public JSONObject getDataById(DbTable table, JSONObject params) {
        DbRow dbRow = builder(table, params);
        this.dialect.preProcessData(table, dbRow.getRow());
        Wrapper<?> wrapper = whereBuilder(table.getColumns(), params, false);
        return DynamicMapperUtils.mapMapper(property, mapper -> mapper.getData(dbRow, wrapper));
    }

    private DbRow builder(DbTable table, JSONObject record) {
        List<DbColumn> columns = table.getColumns();
        JSONObject payload = DbManage.filterJson(columns, record);
        DbRow dbRow = new DbRow();
        dbRow.setTableName(table.getTableName());
        dbRow.setRow(payload);
        return dbRow;
    }

    public QueryWrapper<?> whereBuilder(List<DbColumn> columns, JSONObject record, boolean allFilter) {
        QueryWrapper<?> wrapper = new QueryWrapper<>();
        for (DbColumn column : columns) {
            if (Boolean.TRUE.equals(column.getColKey()) || allFilter) {
                String colName = column.getColName();
                Object value = record.get(colName);
                if (Objects.nonNull(value)) {
                    wrapper.eq(DbDialectUtils.fieldAddQuoting(property.getDbType(), colName), value);
                }
            }
        }
        if (StringUtils.isBlank(wrapper.getCustomSqlSegment()))
            throw new DataException("缺少必须的主键参数");
        return wrapper;
    }

    @Override
    public void close() throws Exception {

    }
}
